package com.balabala.gym_system.service.impl;

import cn.hutool.crypto.digest.DigestAlgorithm;
import cn.hutool.crypto.digest.Digester;
import com.balabala.constant.UserConstant;
import com.balabala.gym_enum.ResultEnum;
import com.balabala.gym_system.constant.AdminConstant;
import com.balabala.gym_system.mapper.AdminMapper;
import com.balabala.gym_system.mapper.AdminRoleMapper;
import com.balabala.gym_system.mapper.AdminVenueMapper;
import com.balabala.gym_system.pojo.dto.*;
import com.balabala.gym_system.pojo.entity.Admin;
import com.balabala.gym_system.pojo.entity.AdminRole;
import com.balabala.gym_system.pojo.entity.AdminVenue;
import com.balabala.gym_system.pojo.vo.AdminRoleVo;
import com.balabala.gym_system.service.AdminService;
import com.balabala.gym_system.service.MenuService;
import com.balabala.gym_system.swagger.utils.JwtUtils;
import com.balabala.result.Result;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.balabala.gym_system.pojo.vo.AdminVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Author:zl
 * Date:2023/5/1 9:26
 */
@Service
@Slf4j
public class AdminServiceImpl extends ServiceImpl<AdminMapper, Admin> implements AdminService {
    @Autowired
    private MenuService menuService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private AdminRoleMapper adminRoleMapper;
    @Autowired
    private AdminVenueMapper adminVenueMapper;


    /**
     * 添加用户
     *
     * @param adminDto
     * @return
     */
    @Override
    public Result insertAdmin(AdminDto adminDto) {
        //1.校验两次密码是否一致
        if (!adminDto.getPassword().equals(adminDto.getPassword2())) {
            log.info("两次密码输入不一致:{}", adminDto.getUsername());
            return Result.fail(ResultEnum.TWO_PASSWORD_ERROR);
            //throw new CustomerException(ExceptionEnum.INCONSISTENT_PASSWORD_INPUT);
        }
        //2.判断用户名，邮箱，电话号码是否重复
        if(!checkUsername(adminDto.getUsername()))
          return   Result.fail(ResultEnum.ADMIN_USED);

        if(!checkEmail(adminDto.getEmail()))
            return   Result.fail(ResultEnum.EMAIL_USED);

        if(!checkPhone(adminDto.getPhone()))
            return   Result.fail(ResultEnum.PHONE_USED);


        //3.拷贝数据到entity
        Admin admin = new Admin();
        BeanUtils.copyProperties(adminDto, admin);

        //4.生成盐
        String salt = UUID.randomUUID().toString();

        //5.密码加密
        Digester md5 = new Digester(DigestAlgorithm.MD5);
        String md5Password = md5.digestHex(admin.getPassword() + salt);

        //6.存储盐和加密后的密码
        admin.setPassword(md5Password);
        admin.setSalt(salt);

        //7.保存到数据库
        this.save(admin);

        //查询出刚刚添加的用户id
        QueryWrapper<Admin> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone", adminDto.getPhone());
        List<Admin> admins = getBaseMapper().selectList(queryWrapper);
        adminDto.setId(admins.get(admins.size() - 1).getId());

        //修改用户对应的场馆表
        this.updateVenue(adminDto);

        //修改用户对应的角色
        this.updateRole(adminDto);

        return Result.ok();
    }

    /**
     * 修改用户信息
     *
     * @param adminDto
     * @return
     */
    @Override
    public Boolean updateById(AdminDto adminDto) {
        Admin admin = new Admin();
        //修改用户对应的场馆表
        this.updateVenue(adminDto);
        //修改用户的角色
        this.updateRole(adminDto);

        BeanUtils.copyProperties(adminDto, admin);
        int i = getBaseMapper().updateById(admin);
        if (i > 0) {
            return true;
        }
        return false;
    }

    /**
     * 启用禁用用户
     *
     * @param updateStatusDto
     * @return
     */
    @Override
    public Boolean updateStatusById(UpdateStatusDto updateStatusDto) {
        Admin admin = new Admin();
        BeanUtils.copyProperties(updateStatusDto, admin);
        int i = getBaseMapper().updateById(admin);

        if (i > 0) {
            return true;
        }
        return false;
    }

    /**
     * 登录
     *
     * @param loginDto
     * @return
     */
    @Override
    public Result login(LoginDto loginDto) {

        //1.根据用户名查询用户信息
        LambdaQueryWrapper<Admin> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Admin::getUsername, loginDto.getUsername());
        Admin admin = getBaseMapper().selectOne(lambdaQueryWrapper);

        if (admin == null) {
            log.info("用户名不存在:{}", loginDto.getUsername());
            return Result.fail(ResultEnum.NO_ADMIN);
        }

        //2.获取盐
        String salt = admin.getSalt();

        //3.再次进行加密
        Digester md5 = new Digester(DigestAlgorithm.MD5);
        String md5Password = md5.digestHex(loginDto.getPassword() + salt);

        //4.使用加密后的密码和数据库加密存储的密码进行对比 如果一致就ok的
        if (!admin.getPassword().equals(md5Password)) {
            log.info("密码错误:{}", loginDto.getUsername());
            return Result.fail(ResultEnum.PASSWORD_ERROR);
        }
        //5.准备载荷数据
        Map<String, Object> map = new HashMap<>();
        map.put("id", admin.getId());
        map.put("username", admin.getUsername());
        map.put("name", admin.getName());
        map.put("phone", admin.getPhone());
        map.put("imageUrl", admin.getImageUrl());
        
        String token = JwtUtils.getToken(map);
        Result result = Result.ok(token);
        //登录后先查询缓存里面有没有用户权限
        Object redisResult = redisTemplate.boundValueOps(AdminConstant.ADMIN_CACHE + admin.getId()).get();
        if (StringUtils.isEmpty(redisResult)) {
            redisTemplate.delete(AdminConstant.ADMIN_CACHE);
        }
        //重新更新缓存
       List<String> permsByUserId = menuService.findPermsByUserId(admin.getId());
        Set<String> set = permsByUserId.stream().collect(Collectors.toSet());
        //3.写入数据到缓存中
        redisTemplate.boundValueOps(UserConstant.USER_PERMISSION + token).set(set);
        //设置失效时间
        redisTemplate.boundValueOps(UserConstant.USER_PERMISSION + token).expire(AdminConstant.ADMIN_CACHE_EXPIRE, TimeUnit.SECONDS);
        return result;
    }


    /**
     * 查询用户以及角色
     *
     * @param adminQueryDto
     * @return
     */
    @Override
    public List<AdminRoleVo> findRole(AdminQueryDto adminQueryDto) {
        return getBaseMapper().findRole(adminQueryDto);

    }

    /**
     * 修改用户角色
     *
     * @param adminUpdateByIdsDto
     * @return
     */
    @Override
    public void adminUpdateByIds(AdminUpdateByIdsDto adminUpdateByIdsDto) {
        LambdaQueryWrapper<AdminRole> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AdminRole::getAdminId, adminUpdateByIdsDto.getAdminId());
        adminRoleMapper.delete(queryWrapper);
        List<Long> list = adminUpdateByIdsDto.getRoleIds();
        if (!StringUtils.isEmpty(list)) {
            log.debug("循环添加管理员角色");
            AdminRole adminRole = new AdminRole();
            //循环添加
            for (int i = 0; i < list.size(); i++) {
                adminRole.setAdminId(adminUpdateByIdsDto.getAdminId());
                adminRole.setRoleId(list.get(i));
                adminRoleMapper.insert(adminRole);
            }
        }

    }

    /**
     * 修改用户场馆
     *
     * @param adminUpdateVenueByIdsDto
     * @return
     */
    @Override
    public void adminUpdateVenueByIds(AdminUpdateVenueByIdsDto adminUpdateVenueByIdsDto) {
        LambdaQueryWrapper<AdminVenue> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AdminVenue::getAdminId, adminUpdateVenueByIdsDto.getAdminId());
        if (adminUpdateVenueByIdsDto.getVenueIds() != null) {
            adminVenueMapper.delete(queryWrapper);
        }
        List<Long> list = adminUpdateVenueByIdsDto.getVenueIds();
        if (!StringUtils.isEmpty(list)) {
            log.debug("循环添加用户场馆id");
            AdminVenue adminVenue = new AdminVenue();
            //循环添加
            for (int i = 0; i < list.size(); i++) {
                adminVenue.setAdminId(adminUpdateVenueByIdsDto.getAdminId());
                adminVenue.setVenueId(list.get(i));
                adminVenueMapper.insert(adminVenue);
            }
        }

    }

    @Override
    public AdminVo findById(Long id) {
       return getBaseMapper().findById(id);
    }

    private Boolean checkUsername(String username) {
        //用用户名查询用户信息，如果查到证明已经存在
        LambdaQueryWrapper<Admin> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Admin::getUsername, username);
        List<Admin> admins = getBaseMapper().selectList(lambdaQueryWrapper);
        System.out.println("查询出用户信息为"+admins);
        if (getBaseMapper().selectList(lambdaQueryWrapper).size()>0) {
            log.info("用户名已经被使用:{}", username);
            return  false;
            //throw new CustomerException(ExceptionEnum.USERNAME_HAS_USED);
        }
        return true;
    }

    private Boolean checkEmail(String email) {
        LambdaQueryWrapper<Admin> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Admin::getEmail, email);

        if (getBaseMapper().selectList(lambdaQueryWrapper).size() >0) {
            log.info("邮箱已经被使用:{}", email);
            return false;
            // throw new CustomerException(ExceptionEnum.EMAIL_HAS_USED);
        }
        return true;
    }

    private Boolean checkPhone(String phone) {
        LambdaQueryWrapper<Admin> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Admin::getPhone, phone);

        if (getBaseMapper().selectList(lambdaQueryWrapper).size()>0) {
            log.info("手机号码已经被使用:{}", phone);
            return false;
            // throw new CustomerException(ExceptionEnum.PHONE_HAS_USED);
        }
        return true;
    }

    /**
     * 修改用户的场馆
     *
     * @param adminDto
     */
    private void updateVenue(AdminDto adminDto) {
        //修改用户对应的场馆表
        AdminUpdateVenueByIdsDto adminUpdateVenueByIdsDto = new AdminUpdateVenueByIdsDto();
        adminUpdateVenueByIdsDto.setAdminId(adminDto.getId());
        adminUpdateVenueByIdsDto.setVenueIds(adminDto.getVenueIds());
        this.adminUpdateVenueByIds(adminUpdateVenueByIdsDto);
    }

    /**
     * 修改用户角色
     */
    private void updateRole(AdminDto adminDto) {
        //修改用户的角色
        AdminUpdateByIdsDto adminUpdateByIdsDto = new AdminUpdateByIdsDto();
        adminUpdateByIdsDto.setAdminId(adminDto.getId());
        adminUpdateByIdsDto.setRoleIds(adminDto.getRoleIds());
        this.adminUpdateByIds(adminUpdateByIdsDto);
    }

}
